use std::collections::{hash_map::Entry, HashMap};

use crate::http_headers::{HttpHeader, HttpHeaderValue};

#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct HttpRequest<'a> {
    // 原始请求
    pub origin_request: &'a str,
    // 请求方法以及地址、协议
    pub request_line: RequestLine<'a>,
    // 请求头
    pub headers: HashMap<&'a str, HttpHeaderValue<'a>>,
    // 请求体
    pub body: String
}

#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct RequestLine<'a> {
    pub method: &'a str,
    pub uri: &'a str,
    pub http_version: &'a str,
}

impl<'a> RequestLine<'a> {
    pub fn new(method: &'a str, uri: &'a str, http_version: &'a str) -> RequestLine<'a> {
        RequestLine {
            method,
            uri,
            http_version
        }
    }
}

impl<'a> HttpRequest<'a> {

    pub fn add_header(&mut self, http_header: HttpHeader<'a>)  {
        if !http_header.name.is_empty() {
            self.headers.insert(http_header.name.trim(), http_header.value);
        }
    }

    #[allow(dead_code)]
    pub fn get_header(&mut self, key: &'a str) -> String {
        match self.headers.entry(key) {
            Entry::Occupied(e) => e.get().to_string(),
            Entry::Vacant(_) => "".to_string()
        }
    }
}

impl<'a> From<&'a str> for RequestLine<'a> {
    fn from(value: &'a str) -> Self {
        let line_parts = value.trim().split_whitespace().filter(|x| !x.is_empty()).collect::<Vec<&str>>();
        RequestLine::new(
            line_parts.get(0).unwrap(),
            line_parts.get(1).unwrap(),
            line_parts.get(2).unwrap())
    }
}

impl<'a> From<&'a str> for HttpRequest<'a> {
    fn from(value: &'a str) -> Self {
        // 解析原始请求数据,首先通过\r\n\r\n来分割出body
        let parts = value.trim().split("\r\n\r\n").filter(|x| !x.is_empty()).map(|x| x.trim()).collect::<Vec<&str>>();
        let request_parts = parts.get(0).unwrap().trim().split("\r\n").filter(|x| !x.is_empty()).map(|x| x.trim()).collect::<Vec<&str>>();
        let request_line_str = request_parts.get(0).unwrap();
        let mut http_request = HttpRequest {
            origin_request: value,
            request_line: From::<&str>::from(request_line_str),
            headers: Default::default(),
            body: match parts.get(1) {
                None => "".to_string(),
                Some(content) => content.to_string()
            },
        };

        let mut maybe_headers_iter = request_parts.into_iter();
        let mut iter_index = 0;
        loop {
            let header_pair = maybe_headers_iter.next();
            match header_pair {
                None => break,
                Some(header_str) => {
                    // 第一行是请求地址等信息,不处理
                    if iter_index > 0 && !header_str.is_empty() {
                        http_request.add_header(From::<&str>::from(header_str));
                    }
                }
            }
            // 标记位+1
            iter_index += 1;
        }

        http_request
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    pub fn test_parse_request() {
        let raw_request = "GET /user-agent HTTP/1.1\r\nHost: localhost:4221\r\nUser-Agent: foobar/1.2.3\r\nAccept: */*\r\n\r\n";
        let http_request = HttpRequest::from(raw_request);
        assert_eq!("GET", http_request.request_line.method);
    }
}